#pragma once

#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <fcntl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>

#include <glog/logging.h>

#include <iomanip>
#include <iosfwd>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <iostream>
#include <fstream>

#include <io.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <fcntl.h>
using google::protobuf::io::FileInputStream;
using google::protobuf::io::FileOutputStream;

#ifdef _MSC_VER
#include <io.h>
#endif

namespace MyUtilitis {
	namespace MyProtoFile {
		using namespace boost::property_tree;  // NOLINT(build/namespaces)
		using google::protobuf::io::FileInputStream;
		using google::protobuf::io::FileOutputStream;
		using google::protobuf::io::ZeroCopyInputStream;
		using google::protobuf::io::CodedInputStream;
		using google::protobuf::io::ZeroCopyOutputStream;
		using google::protobuf::io::CodedOutputStream;
		using google::protobuf::Message;

		inline bool ReadProtoFromTextFile(const char* filename, Message& proto) 
		{
#ifdef _MSC_VER
			int fd = _open(filename, O_RDONLY | O_BINARY);
#else
			int fd = open(filename, O_RDONLY);
#endif
			CHECK_NE(fd, -1) << "File not found: " << filename;
			FileInputStream* input = new FileInputStream(fd);
			bool success = google::protobuf::TextFormat::Parse(input, &proto);
			delete input;
#ifdef _MSC_VER
			_close(fd);
#else
			close(fd);
#endif
			return success;
		}

		inline void WriteProtoToTextFile(Message& proto, const char* filename) 
		{
#ifdef _MSC_VER
			int fd = _open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
#else
			int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
#endif
			FileOutputStream* output = new FileOutputStream(fd);
			CHECK(google::protobuf::TextFormat::Print(proto, output));
			delete output;
#ifdef _MSC_VER
			_close(fd);
#else
			close(fd);
#endif
		}

		inline bool ReadProtoFromBinaryFile(const char* filename, Message& proto) 
		{
#ifdef _MSC_VER
			int fd = _open(filename, O_RDONLY | O_BINARY);
#else
			int fd = open(filename, O_RDONLY);
#endif
			CHECK_NE(fd, -1) << "File not found: " << filename;
			ZeroCopyInputStream* raw_input = new FileInputStream(fd);
			CodedInputStream* coded_input = new CodedInputStream(raw_input);
			coded_input->SetTotalBytesLimit(INT_MAX, 536870912);

			bool success = proto.ParseFromCodedStream(coded_input);

			delete coded_input;
			delete raw_input;
#ifdef _MSC_VER
			_close(fd);
#else
			close(fd);
#endif
			return success;
		}

		inline void WriteProtoToBinaryFile(Message& proto, const char* filename) 
		{
			std::fstream output(filename, std::ios::out | std::ios::trunc | std::ios::binary);
			CHECK(proto.SerializeToOstream(&output));
		}
	}
}